Completed
Push — master ( 5ba72b...438040 )
by Xu
383:15 queued 343:30
created

load-image-exif.js ➔ ... ➔ loadImage.parseExifData   D

Complexity

Conditions 16
Paths 31

Size

Total Lines 91

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 16
c 1
b 0
f 0
nc 31
nop 5
dl 0
loc 91
rs 4.8736

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like load-image-exif.js ➔ ... ➔ loadImage.parseExifData often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*
2
 * JavaScript Load Image Exif Parser
3
 * https://github.com/blueimp/JavaScript-Load-Image
4
 *
5
 * Copyright 2013, Sebastian Tschan
6
 * https://blueimp.net
7
 *
8
 * Licensed under the MIT license:
9
 * https://opensource.org/licenses/MIT
10
 */
11
12
/* global define, Blob */
13
14
;(function (factory) {
15
  'use strict'
16
  if (typeof define === 'function' && define.amd) {
17
    // Register as an anonymous AMD module:
18
    define(['./load-image', './load-image-meta'], factory)
19
  } else if (typeof module === 'object' && module.exports) {
20
    factory(require('./load-image'), require('./load-image-meta'))
21
  } else {
22
    // Browser globals:
23
    factory(window.loadImage)
24
  }
25
})(function (loadImage) {
26
  'use strict'
27
28
  loadImage.ExifMap = function () {
29
    return this
30
  }
31
32
  loadImage.ExifMap.prototype.map = {
33
    Orientation: 0x0112
34
  }
35
36
  loadImage.ExifMap.prototype.get = function (id) {
37
    return this[id] || this[this.map[id]]
38
  }
39
40
  loadImage.getExifThumbnail = function (dataView, offset, length) {
41
    if (!length || offset + length > dataView.byteLength) {
42
      console.log('Invalid Exif data: Invalid thumbnail data.')
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
43
      return
44
    }
45
    return loadImage.createObjectURL(
46
      new Blob([dataView.buffer.slice(offset, offset + length)])
47
    )
48
  }
49
50
  loadImage.exifTagTypes = {
51
    // byte, 8-bit unsigned int:
52
    1: {
53
      getValue: function (dataView, dataOffset) {
54
        return dataView.getUint8(dataOffset)
55
      },
56
      size: 1
57
    },
58
    // ascii, 8-bit byte:
59
    2: {
60
      getValue: function (dataView, dataOffset) {
61
        return String.fromCharCode(dataView.getUint8(dataOffset))
62
      },
63
      size: 1,
64
      ascii: true
65
    },
66
    // short, 16 bit int:
67
    3: {
68
      getValue: function (dataView, dataOffset, littleEndian) {
69
        return dataView.getUint16(dataOffset, littleEndian)
70
      },
71
      size: 2
72
    },
73
    // long, 32 bit int:
74
    4: {
75
      getValue: function (dataView, dataOffset, littleEndian) {
76
        return dataView.getUint32(dataOffset, littleEndian)
77
      },
78
      size: 4
79
    },
80
    // rational = two long values, first is numerator, second is denominator:
81
    5: {
82
      getValue: function (dataView, dataOffset, littleEndian) {
83
        return (
84
          dataView.getUint32(dataOffset, littleEndian) /
85
          dataView.getUint32(dataOffset + 4, littleEndian)
86
        )
87
      },
88
      size: 8
89
    },
90
    // slong, 32 bit signed int:
91
    9: {
92
      getValue: function (dataView, dataOffset, littleEndian) {
93
        return dataView.getInt32(dataOffset, littleEndian)
94
      },
95
      size: 4
96
    },
97
    // srational, two slongs, first is numerator, second is denominator:
98
    10: {
99
      getValue: function (dataView, dataOffset, littleEndian) {
100
        return (
101
          dataView.getInt32(dataOffset, littleEndian) /
102
          dataView.getInt32(dataOffset + 4, littleEndian)
103
        )
104
      },
105
      size: 8
106
    }
107
  }
108
  // undefined, 8-bit byte, value depending on field:
109
  loadImage.exifTagTypes[7] = loadImage.exifTagTypes[1]
110
111
  loadImage.getExifValue = function (
112
    dataView,
113
    tiffOffset,
114
    offset,
115
    type,
116
    length,
117
    littleEndian
118
  ) {
119
    var tagType = loadImage.exifTagTypes[type]
120
    var tagSize
121
    var dataOffset
122
    var values
123
    var i
124
    var str
125
    var c
126
    if (!tagType) {
127
      console.log('Invalid Exif data: Invalid tag type.')
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
128
      return
129
    }
130
    tagSize = tagType.size * length
131
    // Determine if the value is contained in the dataOffset bytes,
132
    // or if the value at the dataOffset is a pointer to the actual data:
133
    dataOffset =
134
      tagSize > 4
135
        ? tiffOffset + dataView.getUint32(offset + 8, littleEndian)
136
        : offset + 8
137
    if (dataOffset + tagSize > dataView.byteLength) {
138
      console.log('Invalid Exif data: Invalid data offset.')
139
      return
140
    }
141
    if (length === 1) {
142
      return tagType.getValue(dataView, dataOffset, littleEndian)
143
    }
144
    values = []
145
    for (i = 0; i < length; i += 1) {
146
      values[i] = tagType.getValue(
147
        dataView,
148
        dataOffset + i * tagType.size,
149
        littleEndian
150
      )
151
    }
152
    if (tagType.ascii) {
153
      str = ''
154
      // Concatenate the chars:
155
      for (i = 0; i < values.length; i += 1) {
156
        c = values[i]
157
        // Ignore the terminating NULL byte(s):
158
        if (c === '\u0000') {
159
          break
160
        }
161
        str += c
162
      }
163
      return str
164
    }
165
    return values
166
  }
167
168
  loadImage.parseExifTag = function (
169
    dataView,
170
    tiffOffset,
171
    offset,
172
    littleEndian,
173
    data
174
  ) {
175
    var tag = dataView.getUint16(offset, littleEndian)
176
    data.exif[tag] = loadImage.getExifValue(
177
      dataView,
178
      tiffOffset,
179
      offset,
180
      dataView.getUint16(offset + 2, littleEndian), // tag type
181
      dataView.getUint32(offset + 4, littleEndian), // tag length
182
      littleEndian
183
    )
184
  }
185
186
  loadImage.parseExifTags = function (
187
    dataView,
188
    tiffOffset,
189
    dirOffset,
190
    littleEndian,
191
    data
192
  ) {
193
    var tagsNumber, dirEndOffset, i
194
    if (dirOffset + 6 > dataView.byteLength) {
195
      console.log('Invalid Exif data: Invalid directory offset.')
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
196
      return
197
    }
198
    tagsNumber = dataView.getUint16(dirOffset, littleEndian)
199
    dirEndOffset = dirOffset + 2 + 12 * tagsNumber
200
    if (dirEndOffset + 4 > dataView.byteLength) {
201
      console.log('Invalid Exif data: Invalid directory size.')
202
      return
203
    }
204
    for (i = 0; i < tagsNumber; i += 1) {
205
      this.parseExifTag(
206
        dataView,
207
        tiffOffset,
208
        dirOffset + 2 + 12 * i, // tag offset
209
        littleEndian,
210
        data
211
      )
212
    }
213
    // Return the offset to the next directory:
214
    return dataView.getUint32(dirEndOffset, littleEndian)
215
  }
216
217
  loadImage.parseExifData = function (dataView, offset, length, data, options) {
218
    if (options.disableExif) {
219
      return
220
    }
221
    var tiffOffset = offset + 10
222
    var littleEndian
223
    var dirOffset
224
    var thumbnailData
225
    // Check for the ASCII code for "Exif" (0x45786966):
226
    if (dataView.getUint32(offset + 4) !== 0x45786966) {
227
      // No Exif data, might be XMP data instead
228
      return
229
    }
230
    if (tiffOffset + 8 > dataView.byteLength) {
231
      console.log('Invalid Exif data: Invalid segment size.')
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
232
      return
233
    }
234
    // Check for the two null bytes:
235
    if (dataView.getUint16(offset + 8) !== 0x0000) {
236
      console.log('Invalid Exif data: Missing byte alignment offset.')
237
      return
238
    }
239
    // Check the byte alignment:
240
    switch (dataView.getUint16(tiffOffset)) {
241
      case 0x4949:
242
        littleEndian = true
243
        break
244
      case 0x4d4d:
245
        littleEndian = false
246
        break
247
      default:
248
        console.log('Invalid Exif data: Invalid byte alignment marker.')
249
        return
250
    }
251
    // Check for the TIFF tag marker (0x002A):
252
    if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002a) {
253
      console.log('Invalid Exif data: Missing TIFF marker.')
254
      return
255
    }
256
    // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
257
    dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian)
258
    // Create the exif object to store the tags:
259
    data.exif = new loadImage.ExifMap()
260
    // Parse the tags of the main image directory and retrieve the
261
    // offset to the next directory, usually the thumbnail directory:
262
    dirOffset = loadImage.parseExifTags(
263
      dataView,
264
      tiffOffset,
265
      tiffOffset + dirOffset,
266
      littleEndian,
267
      data
268
    )
269
    if (dirOffset && !options.disableExifThumbnail) {
270
      thumbnailData = { exif: {} }
271
      dirOffset = loadImage.parseExifTags(
0 ignored issues
show
Unused Code introduced by
The assignment to variable dirOffset seems to be never used. Consider removing it.
Loading history...
272
        dataView,
273
        tiffOffset,
274
        tiffOffset + dirOffset,
275
        littleEndian,
276
        thumbnailData
277
      )
278
      // Check for JPEG Thumbnail offset:
279
      if (thumbnailData.exif[0x0201]) {
280
        data.exif.Thumbnail = loadImage.getExifThumbnail(
281
          dataView,
282
          tiffOffset + thumbnailData.exif[0x0201],
283
          thumbnailData.exif[0x0202] // Thumbnail data length
284
        )
285
      }
286
    }
287
    // Check for Exif Sub IFD Pointer:
288
    if (data.exif[0x8769] && !options.disableExifSub) {
289
      loadImage.parseExifTags(
290
        dataView,
291
        tiffOffset,
292
        tiffOffset + data.exif[0x8769], // directory offset
293
        littleEndian,
294
        data
295
      )
296
    }
297
    // Check for GPS Info IFD Pointer:
298
    if (data.exif[0x8825] && !options.disableExifGps) {
299
      loadImage.parseExifTags(
300
        dataView,
301
        tiffOffset,
302
        tiffOffset + data.exif[0x8825], // directory offset
303
        littleEndian,
304
        data
305
      )
306
    }
307
  }
308
309
  // Registers the Exif parser for the APP1 JPEG meta data segment:
310
  loadImage.metaDataParsers.jpeg[0xffe1].push(loadImage.parseExifData)
311
312
  // Adds the following properties to the parseMetaData callback data:
313
  // * exif: The exif tags, parsed by the parseExifData method
314
315
  // Adds the following options to the parseMetaData method:
316
  // * disableExif: Disables Exif parsing.
317
  // * disableExifThumbnail: Disables parsing of the Exif Thumbnail.
318
  // * disableExifSub: Disables parsing of the Exif Sub IFD.
319
  // * disableExifGps: Disables parsing of the Exif GPS Info IFD.
320
})
321